﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;


public class WebSocket
{
    private ClientWebSocket clientSocket;
    private readonly CancellationToken token = new CancellationTokenSource().Token;
    private ThreadSafeList<byte[]> recvQueue = new ThreadSafeList<byte[]>();
    public bool loginOk = false;
    public bool isOver = false;
    public CallBack ConnError;
    public CallBack ConnOk;

    public async Task Connect(string url)
    {
        this.isOver = false;
        clientSocket = new ClientWebSocket();
        var uri = new Uri(url);
        try
        {
            await clientSocket.ConnectAsync(uri, token);
        }
        catch (Exception ex)
        {
            Console.WriteLine("[网络]ConnError:" + ex.ToString());
            this.CallConnError();
            return;
        }
        StartReceiveMessage();
        if (!this.isOver)
        {
            if (ConnOk != null)
            {
                ConnOk();
            }
        }
    }

    /// <summary>
    /// 接受数据
    /// </summary>
    private async void StartReceiveMessage()
    {
        var receiveBuf = new byte[1024 * 1024];
        var isClosed = false;
        var curPos = 0;

        try
        {
            while (!isClosed)
            {
                try
                {
                    var left = receiveBuf.Length - curPos;
                    if (left < 0)
                    {
                        var temp = new byte[receiveBuf.Length * 2];
                        Array.Copy(receiveBuf, temp, receiveBuf.Length);
                        receiveBuf = temp;
                        left = receiveBuf.Length - curPos;
                    }

                    var result = await clientSocket.ReceiveAsync(new ArraySegment<byte>(receiveBuf, curPos, left), token);
                    switch (result.MessageType)
                    {
                        case WebSocketMessageType.Binary:
                            break;
                        case WebSocketMessageType.Close:
                            isClosed = true;
                            Console.WriteLine("[网络]对方主动关闭网络");
                            this.CallConnError();
                            return;
                        case WebSocketMessageType.Text:
                            isClosed = true;
                            Console.WriteLine("[网络]接受到了text");
                            this.CallConnError();
                            return;
                        default:
                            isClosed = true;
                            Console.WriteLine("[网络]接受到了未知东西");
                            this.CallConnError();
                            return;
                    }


                    curPos += result.Count;
                    if (result.EndOfMessage)
                    {//完成一个消息的接受
                        byte[] bytes = new byte[curPos];
                        Array.Copy(receiveBuf, bytes, curPos);
                        recvQueue.write.Add(bytes);
                        //===重置
                        receiveBuf = new byte[1024 * 1024];
                        curPos = 0;
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                    return;
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("[网络]错误:" + ex.ToString() + ",isOver:" + isOver);
            this.CallConnError();
        }
    }

    public void Recv(List<byte[]> datas)
    {
        if (recvQueue.write.Count > 0)
        {
            recvQueue.exchange();
            datas.AddRange(recvQueue.read);
            recvQueue.Clear();
        }
    }

    public void Send(byte[] data)
    {
        if (this.isOver) { return; }
        try
        {
            clientSocket.SendAsync(new ArraySegment<byte>(data), WebSocketMessageType.Binary, true, token);
        }
        catch
        {
            Console.WriteLine("[网络]发送数据错误");
            this.CallConnError();
        }
    }
    private void CallConnError()
    {
        if (isOver) { return; }
        if (this.ConnError != null)
        {
            this.ConnError();
        }
    }


    public async void clear()
    {
        this.isOver = true;
        this.loginOk = false;
        if (this.clientSocket != null)
        {
            try
            {
                await this.clientSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "NormalClosure", token);
            }
            catch
            {

            }
            this.clientSocket = null;
        }
        //this.clearHeart();
    }
}

public class ThreadSafeList<T>
{
    public List<T> write;
    public List<T> read;

    public List<T> obj1;
    public List<T> obj2;

    private object obj = new object();

    public ThreadSafeList()
    {
        obj1 = new List<T>();
        obj2 = new List<T>();

        write = obj1;
        read = obj2;
    }

    public int Add(T t)
    {
        int k = 0;
        lock (obj)
        {
            write.Add(t);
            k = write.Count;
        }
        return k;
    }

    public void Add(List<T> ts)
    {
        lock (obj)
        {
            write.AddRange(ts);
        }
    }

    public int Count
    {
        get
        {
            return this.write.Count;
        }
    }

    public bool hasData
    {
        get
        {
            return this.write.Count > 0;
        }
    }

    public void exchange()
    {
        lock (obj)
        {
            List<T> v = write;
            write = read;
            read = v;
        }
    }

    public void Clear()
    {
        lock (obj)
        {
            write.Clear();
            read.Clear();
        }
    }
}